package com.rock.securityresearch.service

import cats.Parallel
import cats.effect.Sync
import com.rock.securityresearch.common.TimePeriod
import com.rock.securityresearch.domain.{DateRange, RangeType}
import com.rock.securityresearch.equity.{HistoricalStockQuote, Stocks}
import com.rock.securityresearch.equity.StockCode.StockCode
import com.rock.securityresearch.infracture.SearchResultItem
import com.rock.securityresearch.util.HistoricalStockQuoteUtil
import org.typelevel.log4cats.Logger
import com.rock.securityresearch.infracture.Netease

import java.time.LocalDate
import scala.collection.immutable.List

trait StockService[F[_]] {
    def getQuotePrice(codes: List[StockCode], range: DateRange, rangeType: RangeType,
                      period: TimePeriod = TimePeriod.Daily): F[Map[String, Option[List[HistoricalStockQuote]]]]

    def searchStock(word: String): F[List[SearchResultItem]]

}

object StockService {

    import cats.effect.IO
    import com.rock.securityresearch.equity.{HistoricalStockQuote, Stocks}
    import cats.syntax.parallel.*
    import cats.syntax.functor.toFunctorOps

    def make[F[_] : Sync : Logger](stocks: Stocks[F])(using P: Parallel[F]): StockService[F] = {
        new StockService[F] {
            override def getQuotePrice(codes: List[StockCode], oriRange: DateRange, rangeType: RangeType, period: TimePeriod = TimePeriod.Daily): F[Map[String, Option[List[HistoricalStockQuote]]]] = {
                val range = DateRange.generate(rangeType, oriRange)
                val resultsIo = codes.parTraverse { code =>
                    stocks.getQuotePrice(code, range).map(e => (code, e.map(quotes => {
                        groupByPeriods(takeBy(quotes, rangeType), period)
                    })))
                }
                resultsIo.map { quoteList =>
                    val resultMap = scala.collection.mutable.Map.empty[String, Option[List[HistoricalStockQuote]]]
                    quoteList.foreach { e =>
                        resultMap += e._1.code -> e._2
                    }
                    resultMap.toMap
                }
            }
            override def searchStock(word: String): F[List[SearchResultItem]] = {
                Netease.search(word)
            }
            private def groupByPeriods(quotes: List[HistoricalStockQuote], period: TimePeriod): List[HistoricalStockQuote] = {
                HistoricalStockQuoteUtil.getStockQuote(quotes, period)
            }
        }
    }

    private def takeBy(list: List[HistoricalStockQuote], rangeType: RangeType): List[HistoricalStockQuote] ={
        val res = rangeType match
                    case RangeType.Custom => list
                    case RangeType.Days5 => list.take(5)
                    case RangeType.Days20 => list.take(20)
                    case RangeType.Days60 => list.take(60)
        res
    }
}